package br.ufrn.btest.tests.unit;

import static org.junit.Assert.assertEquals;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.junit.Test;

import br.ufrn.btest.BlockBuilder;
import br.ufrn.btest.Partitioner;
import br.ufrn.btest.parser.Machine;
import br.ufrn.btest.parser.Operation;
import de.be4.classicalb.core.parser.BException;

public class BlockBuilderTest {

	@Test
	public void shouldFormulateBlocks() {
		Machine machine = getMachineInstance("machines/Paperround.mch");
		Operation operationUnderTest = machine.getOperation(0);
		BlockBuilder blockBuilder = new BlockBuilder(new Partitioner(operationUnderTest));
		
		HashMap<String, List<String>> expectedBlocks = new HashMap<String, List<String>>();
		
		// Invariant blocks
		List<String> q1Blocks = new ArrayList<String>();
		q1Blocks.add("papers <: -4..0");
		q1Blocks.add("papers <: 164..168");
		q1Blocks.add("papers <: 1..163");
		
		List<String> q2Blocks = new ArrayList<String>();
		q2Blocks.add("magazines <: papers");
		
		List<String> q3Blocks = new ArrayList<String>();
		q3Blocks.add("card(papers) <= 60");
		
		
		// Precondition blocks
		List<String> q4Blocks = new ArrayList<String>();
		q4Blocks.add("hh : -4..0");
		q4Blocks.add("hh : 164..168");
		q4Blocks.add("hh : 1..163");

		List<String> q5Blocks = new ArrayList<String>();
		q5Blocks.add("card(papers) < 60");
		q5Blocks.add("not(card(papers) < 60)");

		
		expectedBlocks.put("papers <: 1..163", q1Blocks);
		expectedBlocks.put("magazines <: papers", q2Blocks);
		expectedBlocks.put("card(papers) <= 60", q3Blocks);

		expectedBlocks.put("hh : 1..163", q4Blocks);
		expectedBlocks.put("card(papers) < 60", q5Blocks);

		assertEquals(expectedBlocks, blockBuilder.getEquivalentClassesBlocks());
	}
	
	
	
	@Test
	public void shouldFormulateBlocks2() {
		Machine machine = getMachineInstance("machines/Player.mch");
		Operation operationUnderTest = machine.getOperation(0);
		BlockBuilder blockBuilder = new BlockBuilder(new Partitioner(operationUnderTest));
		
		Map<String, List<String>> expectedBlocks = new HashMap<String, List<String>>();
		
		// Invariant Blocks
		List<String> ch1Blocks = new ArrayList<String>();
		ch1Blocks.add("team <: PLAYER");
		
		List<String> ch2Blocks = new ArrayList<String>();
		ch2Blocks.add("card(team) = 11");
	
		
		// Precondition Blocks
		List<String> ch3Blocks = new ArrayList<String>();
		ch3Blocks.add("pp : team");
		ch3Blocks.add("not(pp : team)");
		
		List<String> ch4Blocks = new ArrayList<String>();
		ch4Blocks.add("rr : PLAYER");

		List<String> ch5Blocks = new ArrayList<String>();
		ch5Blocks.add("rr /: team");
		ch5Blocks.add("not(rr /: team)");
		
		
		expectedBlocks.put("team <: PLAYER", ch1Blocks);
		expectedBlocks.put("card(team) = 11", ch2Blocks);
		expectedBlocks.put("pp : team", ch3Blocks);
		expectedBlocks.put("rr : PLAYER", ch4Blocks);
		expectedBlocks.put("rr /: team", ch5Blocks);
		
		assertEquals(expectedBlocks, blockBuilder.getEquivalentClassesBlocks());
	}
	
	
	
	@Test
	public void shouldFormulateBlocksWhenOperationtHasConditionals() {
		Machine machine = getMachineInstance("machines/Player.mch");
		Operation operationUnderTest = machine.getOperation(1);
		BlockBuilder blockBuilder = new BlockBuilder(new Partitioner(operationUnderTest));
		
		Map<String, List<String>> expectedBlocks = new HashMap<String, List<String>>();
		
		
		// Invariant Blocks
		List<String> ch1Blocks = new ArrayList<String>();
		ch1Blocks.add("team <: PLAYER");
		
		List<String> ch2Blocks = new ArrayList<String>();
		ch2Blocks.add("card(team) = 11");

		
		// Precondition Blocks
		List<String> ch3Blocks = new ArrayList<String>();
		ch3Blocks.add("pp : PLAYER");

		
		// Conditional Blocks
		List<String> ch4Blocks = new ArrayList<String>();
		ch4Blocks.add("pp : team");
		ch4Blocks.add("not(pp : team)");

		expectedBlocks.put("team <: PLAYER", ch1Blocks);
		expectedBlocks.put("card(team) = 11", ch2Blocks);
		expectedBlocks.put("pp : PLAYER", ch3Blocks);
		expectedBlocks.put("pp : team", ch4Blocks);
		
		assertEquals(expectedBlocks, blockBuilder.getEquivalentClassesBlocks());
	}
	
	
	
	@Test
	public void shouldFormulateBlocksToIntervals() {
		Machine machine = getMachineInstance("machines/Intervaltest.mch");
		Operation operationUnderTest = machine.getOperation(0);
		BlockBuilder blockBuilder = new BlockBuilder(new Partitioner(operationUnderTest));
		
		HashMap<String, List<String>> expectedBlocks = new HashMap<String, List<String>>();
		
		List<String> c1Blocks = new ArrayList<String>(); 
		c1Blocks.add("aa : 0..4");
		c1Blocks.add("aa : 11..15");
		c1Blocks.add("aa : 5..10");
		
		expectedBlocks.put("aa : 5..10", c1Blocks);
		
		assertEquals(expectedBlocks, blockBuilder.getEquivalentClassesBlocks());
	}
	
	
	
	@Test
	public void shouldFormulateBlocksForBoundaryValues() {
		Machine machine = getMachineInstance("machines/Paperround.mch");
		Operation operationUnderTest = machine.getOperation(0);
		BlockBuilder blockBuilder = new BlockBuilder(new Partitioner(operationUnderTest));
		
		Map<String, List<String>> expectedBlocks = new HashMap<String, List<String>>();
		
		// Invariant Blocks
		List<String> ch1Blocks = new ArrayList<String>();
		ch1Blocks.add("papers = {0}");
		ch1Blocks.add("papers = {2}");
		ch1Blocks.add("papers = {162}");
		ch1Blocks.add("papers = {164}");
		
		List<String> ch2Blocks = new ArrayList<String>();
		ch2Blocks.add("magazines <: papers");
		
		List<String> ch3Blocks = new ArrayList<String>();
		ch3Blocks.add("card(papers) <= 60");
		
		
		// Precondition Blocks
		List<String> ch4Blocks = new ArrayList<String>();
		ch4Blocks.add("hh = 0");
		ch4Blocks.add("hh = 2");
		ch4Blocks.add("hh = 162");
		ch4Blocks.add("hh = 164");
		
		List<String> ch5Blocks = new ArrayList<String>();
		ch5Blocks.add("card(papers) < 60");
		ch5Blocks.add("not(card(papers) < 60)");

		
		expectedBlocks.put("papers <: 1..163", ch1Blocks);
		expectedBlocks.put("magazines <: papers", ch2Blocks);
		expectedBlocks.put("card(papers) <= 60", ch3Blocks);
		expectedBlocks.put("hh : 1..163", ch4Blocks);
		expectedBlocks.put("card(papers) < 60", ch5Blocks);
		
		assertEquals(expectedBlocks, blockBuilder.getBoundaryValueBlocks());
	}
	
	
	
	@Test
	public void shouldFormulateBlocksAsLists() {
		Machine machine = getMachineInstance("machines/Paperround.mch");
		Operation operationUnderTest = machine.getOperation(0);
		BlockBuilder blockBuilder = new BlockBuilder(new Partitioner(operationUnderTest));
		
		List<List<String>> blocksLists = new ArrayList<List<String>>();
		
		List<String> block1Values = new ArrayList<String>();
		block1Values.add("magazines <: papers");
		
		List<String> block2Values = new ArrayList<String>();
		block2Values.add("hh : -4..0");
		block2Values.add("hh : 164..168");
		block2Values.add("hh : 1..163");
		
		List<String> block3Values = new ArrayList<String>();
		block3Values.add("card(papers) <= 60");
		
		List<String> block4Values = new ArrayList<String>();
		block4Values.add("papers <: -4..0");
		block4Values.add("papers <: 164..168");
		block4Values.add("papers <: 1..163");
		
		List<String> block5Values = new ArrayList<String>();
		block5Values.add("card(papers) < 60");
		block5Values.add("not(card(papers) < 60)");

		blocksLists.add(block1Values);
		blocksLists.add(block2Values);
		blocksLists.add(block3Values);
		blocksLists.add(block4Values);
		blocksLists.add(block5Values);
		
		assertEquals(blocksLists, blockBuilder.getEquivalentClassesBlocksAsLists());
	}
	

	
	@Test
	public void shouldNotNegateTypingClauses() {
		Machine machine = getMachineInstance("machines/NegationTest.mch");
		Operation operationUnderTest = machine.getOperation(0);
		Partitioner partitioner = new Partitioner(operationUnderTest);
		BlockBuilder blockBuilder = new BlockBuilder(partitioner);
		
		Set<String> typingClauses = partitioner.getTypingClauses();
		
		Set<String> expectedTypingClauses = new HashSet<String>();
		expectedTypingClauses.add("aa : INT");
		expectedTypingClauses.add("bb : BOOL");
		expectedTypingClauses.add("cc : STRING");
		expectedTypingClauses.add("dd : ID");
		expectedTypingClauses.add("ee : DEF");
		expectedTypingClauses.add("ff : (INT * INT)");
		expectedTypingClauses.add("gg : POW(INT)");
		
		assertEquals(expectedTypingClauses, typingClauses);
		
		HashMap<String, List<String>> expectedBlocks = new HashMap<String, List<String>>();
		
		List<String> c1Blocks = new ArrayList<String>();
		c1Blocks.add("aa : INT");
		List<String> c2Blocks = new ArrayList<String>();
		c2Blocks.add("bb : BOOL");
		List<String> c3Blocks = new ArrayList<String>();
		c3Blocks.add("cc : STRING");
		List<String> c4Blocks = new ArrayList<String>();
		c4Blocks.add("dd : ID");
		List<String> c5Blocks = new ArrayList<String>();
		c5Blocks.add("ee : DEF");
		List<String> c6Blocks = new ArrayList<String>();
		c6Blocks.add("ff : (INT * INT)");
		List<String> c7Blocks = new ArrayList<String>();
		c7Blocks.add("gg : POW(INT)");
		
		expectedBlocks.put("aa : INT", c1Blocks);
		expectedBlocks.put("bb : BOOL", c2Blocks);
		expectedBlocks.put("cc : STRING", c3Blocks);
		expectedBlocks.put("dd : ID", c4Blocks);
		expectedBlocks.put("ee : DEF", c5Blocks);
		expectedBlocks.put("ff : (INT * INT)", c6Blocks);
		expectedBlocks.put("gg : POW(INT)", c7Blocks);
		
		assertEquals(expectedBlocks, blockBuilder.getEquivalentClassesBlocks());
	}
	
	
	
	private Machine getMachineInstance(String path) {
		Machine machine;
		try {
			machine = new Machine(new File(path));
		} catch (BException e) {
			machine = null;
		}
		return machine;
	}
}
